/*
 *  Copyright 2012 Zdeněk Janeček

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

 *    http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kiv.pt.ui;

import java.awt.geom.Point2D;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Random;
import java.io.FileReader;
import java.util.List;

import kiv.pt.DataGuru;
import kiv.pt.OdberneMisto;
import kiv.pt.PowController;
import kiv.pt.Simulator;
import kiv.pt.ZdrojEnergie;

/**
 * Textové uživatelské prostředí.
 * @author Zdeněk Janeček
 */
public class TUI extends Thread {
    private static final char ESC = 27;
    private static final int SC_SUMMARY = 31;
    private static final int SC_DETAIL = 32;
    
    private final PowController pc;
    private final Simulator sim;
    
    private int screen = SC_SUMMARY;
    private boolean reading = true;
    private boolean changing = false;
    
    private String newInfo;
    private int place = 0;
    private int source = 0;
    
    private final String[] ascii = new String[3];
    private int state = 0;

    /**
     * Zajišťuje výpis na terminál.
     * @param pc Instance třídy {@code PowController}, která posktuje informace.
     * @param sim Instance třídy {@code Simulator}, kterou UI řídí.
     */
    public TUI(PowController pc, Simulator sim) {
        this.pc = pc;
        this.sim = sim;
        
        int var = 0;
        try {
//            InputStream asciiImage = getClass().getResourceAsStream("/ascii");
            FileReader asciiImage = new FileReader("../ascii");
            StringBuilder sb = new StringBuilder();
            int znak;
            while ((znak = asciiImage.read()) != -1) {
                if (znak == ';') {
                    ascii[var++] = sb.toString();
                    sb = new StringBuilder();
                    asciiImage.read();
                    continue;
                }
                
                sb.append((char) znak);
            }
            ascii[var] = sb.toString();
            
            asciiImage.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private void conPrint(String s, int row, int column) {
        int rowl = row;
        System.out.print(ESC + "[" + rowl + ";" + column + "H");
        for (char c : s.toCharArray()) {
            if (c == '\n') {
                System.out.print(ESC + "[" + ++rowl + ";" + column + "H");
                continue;
            }
            System.out.print(c);
        }
    }
    
    private int readNumber(InputStream is) throws IOException {
        char[] s = new char[is.available()-1];
        int i = 0;
        char znak;
        while (is.available() > 1) {
            znak = (char) is.read();
            if (znak >= '0' && znak <= '9') {
                s[i++] = znak;
            } else {
                break;
            }
        }
        
        if (i == 0) {
            return -1;
        }
        
        return Integer.parseInt(new String(s).substring(0, i));
    }
    
    private void redraw() {
        System.out.print(ESC + "[44;37m");
        System.out.print(ESC + "[2J");
        System.out.print(ESC + "[1;1H");
        System.out.flush();
        System.out.print("┌──────────────────────────────────────────────────────────────────────────────┐");
        for (int i = 2; i < 20; i++) {
            System.out.print(ESC + "[" + i + ";1H");
            System.out.print('│');
            System.out.print(ESC + "[" + i + ";80H");
            System.out.print('│');
        }
        System.out.print("\n│ ");
        System.out.print(ESC + "[1m");
        System.out.print("Zdeněk Janeček a Ondřej Ptáček 2012");
        System.out.print(ESC + "[0m");
        System.out.print(ESC + "[44;37m");
        System.out.print(ESC + "[20;80H");
        System.out.print('│');
        System.out.print("\n└──────────────────────────────────────────────────────────────────────────────┘");
        System.out.print(ESC + "[2;1H");
    }

    /**
     * Slouží k zasílání zpráv na obrazovku.
     * @param s Běžný řetězec sloužící k informování.
     * @param err Řetězec varující při chybách, nebo jiný co potřebuje
     * upoutat pozornost.
     */
    public synchronized void changeScreen(String s, String err) {
        if (err == null) {
            newInfo = s;
        } else {
            newInfo = s+"\n"+ESC+"[6m"+ESC+"[31m"+err+ESC+"[37m"+ESC+"[25m";
        }
        changing = true;
    }
    
    private boolean parseVstup(InputStream in) throws IOException {
        int val = in.read();
        
        switch (val) {
        case 'q':
            sim.zastav();
            reading = false;
            break;
        case '1':
            screen = SC_SUMMARY;
            changing = true;
            break;
        case '2':
            screen = SC_DETAIL;
            changing = true;
            break;
        case 'm':
            val = readNumber(in);
            if (val < DataGuru.mista.size() && val >= 0) {
                place = val;
            } else if (val == -1) {
                return true;
            }
            
            changing = true;
            break;
        case 'z':
            val = readNumber(System.in);
            if (val < DataGuru.zdroje.size() && val >= 0) {
                source = val;
            } else if (val == -1) {
                return true;
            }
            
            changing = true;
            break;
        case 'p':
            sim.togglePlay();
            changing = true;
        case 'n':
            int type = readNumber(System.in);
            if (type > -1) {
                Random r = new Random(System.currentTimeMillis());
                int newId = DataGuru.mista.size();
                Point2D.Double bod = new Point2D.Double(r.nextFloat()*100, r.nextFloat()*100);
            
                boolean firma = false;
                if (type == 'f') {
                    firma = true;
                    DataGuru.pocetFirem++;
                } else {
                    DataGuru.pocetDom++;
                }
                OdberneMisto nove = new OdberneMisto(bod, 0, newId, firma, 500);
                DataGuru.mista.add(nove);
                int s = 0;
                double dist = Double.POSITIVE_INFINITY;
                for (ZdrojEnergie ze : DataGuru.zdroje) {
                    double newDist = DataGuru.distanceBetweenFast(ze.getCoordinates().x, ze.getCoordinates().y, bod.x, bod.y);
                    if (dist > newDist) {
                        s = ze.getId();
                        dist = newDist;
                    }
                }
                DataGuru.zdroje.get(s).addSoused(newId);
                
                pc.cenaCelk = Arrays.copyOf(pc.cenaCelk, DataGuru.mista.size());
                pc.mistaCelk = Arrays.copyOf(pc.mistaCelk, DataGuru.mista.size());
                
                DataGuru.calculateDistances();
                
                changing = true;
            }
        case 'a':
            val = readNumber(System.in);
            int vyk = readNumber(System.in);
            if (val < DataGuru.mista.size() && val >= 0) {
                DataGuru.mista.get(val).setiPrikon((double) vyk);
            } else if (val == -1) {
                return true;
            }
            
            changing = true;
        case 't':
            val = readNumber(System.in);
            sim.setTime(val);
        default:
            break;
        }
        return false;
    }
    
    private void printText() {
        switch (screen) {
        case SC_SUMMARY:
            conPrint("Počet míst: " + DataGuru.mista.size(), 2, 2);
            conPrint("Počet zdrojů: " + DataGuru.zdroje.size(), 3, 2);
            conPrint("q: Vypnout\n1: Hlavní obrazovka\n2: Detail\nm#: změní místo\nz#: změní zdroj\np: pozastavý/spustí\na#,#: nastavý první id na hodnotu druhou\nn{f,d}: přidá firmu/domácnost\nt#: nastavý čas", 5, 4);
            System.out.flush();
            System.out.print(ESC + "[5;20H");
            
            conPrint(newInfo, 2, 25);
            
            String fancy = ascii[state];
            state = (state + 1) % ascii.length;
            conPrint(fancy, 6, 50);
            
            System.out.print(ESC + "[18;2H");
            System.out.flush();
            break;
        case SC_DETAIL:
            conPrint(newInfo, 2, 2);
            double prikon = DataGuru.mista.get(place).getPrikon();
            conPrint("Místo " + place + " odebírá " + prikon+" kW", 4, 2);
            conPrint(" * Zaplatí "
                            + prikon
                            * (pc.suppliers[place] == 0 ? PowController.PRICE_MAIN
                                    : PowController.PRICE_ALT)+" Kč", 5, 2);
            conPrint(" * Odebírá od zdroje "+pc.suppliers[place], 6, 2);
            conPrint(" * Typ: "+(DataGuru.mista.get(place).isFirma() ? "Firma" : "Domácnost"), 7, 2);
            conPrint(" * Vzdálenost od zdroje: "+pc.dists[place]+" km", 8, 2);
            
            double vykon = DataGuru.zdroje.get(source).getVykon();
            conPrint("Zdroj "+source, 9, 2);
            conPrint(" * Výkon je "+vykon+" kW", 10, 2);
            conPrint(" * Využití je nyní "+pc.zateze[source]+" kW", 11, 2);
            conPrint(" * Rozdíl výkon - využití = "+(vykon - pc.zateze[source])+" kW", 12, 2);
            conPrint(" * Pozice "+DataGuru.zdroje.get(source).getCoordinates(), 13, 2);
            break;
        default:
            break;
        }
    }
    
    @Override
    public void run() {
        while (reading) {
            try {
                if (System.in.available() > 0) {
                    if (parseVstup(System.in)) {
                        continue;
                    }
                } else {
                    sleep(200);
                }
                
                if (changing) {
                    redraw();
                    printText();
                    
                    conPrint("vstup: ", 18, 2);
                    changing = false;
                }
            } catch (IOException e) {
                System.out.println("!!! chyba vstupu/výstupu");
            } catch (InterruptedException e) {
                System.out.println("!!! chyba běhu vlákna");
            }
        }
        
        System.out.print((char) 27 + "[0m");
    }
}
